'use strict'

const assert = require('node:assert/strict')

const { afterEach, beforeEach, describe, it } = require('mocha')
const sinon = require('sinon')
const sensitiveHandler =
  require('../../../../../src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler')
const { DEFAULT_IAST_REDACTION_NAME_PATTERN, DEFAULT_IAST_REDACTION_VALUE_PATTERN } =
  require('../../../../../src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex')

const { suite } = require('../resources/evidence-redaction-suite.json')

function doTest (testCase, parameter) {
  const description = testCase.description.split(parameter.name).join(parameter.value)
  const input = JSON.parse(JSON.stringify(testCase.input).split(parameter.name).join(parameter.value))

  it(description, () => {
    input.forEach(source => {
      const result = sensitiveHandler.isSensibleSource(source)
      assert.strictEqual(result, true)
    })
  })
}

describe('Sensitive handler', () => {
  beforeEach(() => {
    sensitiveHandler.setRedactionPatterns(DEFAULT_IAST_REDACTION_NAME_PATTERN, DEFAULT_IAST_REDACTION_VALUE_PATTERN)
  })

  describe('Custom redaction patterns', () => {
    describe('Default redaction patterns', () => {
      it('should use default patterns when null ones are set', () => {
        sensitiveHandler.setRedactionPatterns(null, null)
        assert.strictEqual(sensitiveHandler._namePattern.source, DEFAULT_IAST_REDACTION_NAME_PATTERN)
        assert.strictEqual(sensitiveHandler._valuePattern.source, DEFAULT_IAST_REDACTION_VALUE_PATTERN)
      })

      it('should use default name pattern when custom name pattern is null', () => {
        const customValuePattern = 'valuePattern'
        sensitiveHandler.setRedactionPatterns(null, customValuePattern)
        assert.strictEqual(sensitiveHandler._namePattern.source, DEFAULT_IAST_REDACTION_NAME_PATTERN)
        assert.strictEqual(sensitiveHandler._valuePattern.source, customValuePattern)
      })

      it('should use default value pattern when custom value pattern is null', () => {
        const customNamePattern = 'namePattern'
        sensitiveHandler.setRedactionPatterns(customNamePattern, null)
        assert.strictEqual(sensitiveHandler._namePattern.source, customNamePattern)
        assert.strictEqual(sensitiveHandler._valuePattern.source, DEFAULT_IAST_REDACTION_VALUE_PATTERN)
      })
    })

    describe('Not valid custom patterns', () => {
      const log = require('../../../../../src/log')

      beforeEach(() => {
        sinon.stub(log, 'warn')
      })

      afterEach(() => {
        sinon.restore()
      })

      it('should use default patterns when not valid ones are set', () => {
        sensitiveHandler.setRedactionPatterns('(unterminated_group', 'unmatched)')
        assert.strictEqual(sensitiveHandler._namePattern.source, DEFAULT_IAST_REDACTION_NAME_PATTERN)
        assert.strictEqual(sensitiveHandler._valuePattern.source, DEFAULT_IAST_REDACTION_VALUE_PATTERN)

        sinon.assert.calledTwice(log.warn)
        assert.strictEqual(log.warn.firstCall.args[0], '[ASM] Redaction name pattern is not valid')
        assert.strictEqual(log.warn.secondCall.args[0], '[ASM] Redaction value pattern is not valid')
      })

      it('should use default name pattern when custom name pattern is not valid', () => {
        const customValuePattern = 'valuePattern'
        sensitiveHandler.setRedactionPatterns('(unterminated_group', customValuePattern)
        assert.strictEqual(sensitiveHandler._namePattern.source, DEFAULT_IAST_REDACTION_NAME_PATTERN)
        assert.strictEqual(sensitiveHandler._valuePattern.source, customValuePattern)

        sinon.assert.calledOnceWithExactly(log.warn, '[ASM] Redaction name pattern is not valid')
      })

      it('should use default value pattern when custom value pattern is not valid', () => {
        const customNamePattern = 'namePattern'
        sensitiveHandler.setRedactionPatterns(customNamePattern, 'unmatched)')
        assert.strictEqual(sensitiveHandler._namePattern.source, customNamePattern)
        assert.strictEqual(sensitiveHandler._valuePattern.source, DEFAULT_IAST_REDACTION_VALUE_PATTERN)

        sinon.assert.calledOnceWithExactly(log.warn, '[ASM] Redaction value pattern is not valid')
      })
    })

    it('Valid custom patterns', () => {
      assert.strictEqual(sensitiveHandler.isSensibleName('sensibleName'), false)
      assert.strictEqual(sensitiveHandler.isSensibleValue('sensibleValue'), false)

      sensitiveHandler.setRedactionPatterns('sensibleName', 'sensibleValue')

      assert.strictEqual(sensitiveHandler.isSensibleName('sensibleName'), true)
      assert.strictEqual(sensitiveHandler.isSensibleValue('sensibleValue'), true)
    })
  })

  describe('Sensible sources', () => {
    suite.filter(testCase => testCase.type === 'SOURCES').forEach((testCase) => {
      for (const name in testCase.parameters) {
        testCase.parameters[name].forEach(value => {
          doTest(testCase, { name, value })
        })
      }
    })
  })
})
